﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using vJoyInterfaceWrap;
using XboxOneDriver;
using XboxOneDriverApp.Properties;

namespace XboxOneDriverApp
{
    public partial class MainForm : Form
    {
        public const string userFile = "userdata.user";

        private vJoy[] joysticks;
        private vJoy.JoystickState iReport;
        private PictureBox[] gamepadStatus;

        public bool triggerButtons;
        private bool loaded;

        public string[] ExcludeProcesses = new string[]
        {
            "chrome", "explorer", "dllhost", "firefox", "xboxonedriver", "xboxonedriver.vshost",
            "origin"
        };

        public MainForm()
        {
            joysticks = new vJoy[16];
            gamepadStatus = new PictureBox[16];

            InitializeComponent();
        }

        private void ReloadList()
        {
            list_games.Items.Clear();

            if (File.Exists(userFile))
            {
                using (Stream s = File.Open(userFile, FileMode.Open))
                {
                    UserData.Read(s);
                }

                for (int i = 0; i < UserData.gameFolders.Count; i++)
                {
                    string folder = UserData.gameFolders[i];
                    // search for executables in folder
                    DirectoryInfo dir = new DirectoryInfo(folder);
                    FileInfo[] files = dir.GetFiles("*.exe", SearchOption.AllDirectories);
                    for (int j = 0; j < files.Length; j++)
                    {
                        FileInfo f = files[j];
                        list_games.Items.Add(f);
                    }
                }
            }
        }


        private void MainForm_Load(object sender, EventArgs e)
        {
            gamepadStatus[0] = gamePad1;
            gamepadStatus[1] = gamePad2;
            gamepadStatus[2] = gamePad3;
            gamepadStatus[3] = gamePad4;
            gamepadStatus[4] = gamePad5;
            gamepadStatus[5] = gamePad6;
            gamepadStatus[6] = gamePad7;
            gamepadStatus[7] = gamePad8;
            gamepadStatus[8] = gamePad9;
            gamepadStatus[9] = gamePad10;
            gamepadStatus[10] = gamePad11;
            gamepadStatus[11] = gamePad12;
            gamepadStatus[12] = gamePad13;
            gamepadStatus[13] = gamePad14;
            gamepadStatus[14] = gamePad15;
            gamepadStatus[15] = gamePad16;

            XboxOneGamepad.InitializeAll();

            for (int i = 0; i < 16; i++)
            {
                XboxOneGamepad g = XboxOneGamepad.GetGamepad(i);
                g.OnConnectedChanged += g_OnConnectedChanged;
                g.OnDataArrive += g_OnDataArrive;

                if (g.Connected)
                {
                    gamepadStatus[i].Image = Resources.xbox_one_logo;
                }
                else
                {
                    gamepadStatus[i].Image = Resources.xbox_one_logo_dark;
                }
            }

            button1_Click(null, EventArgs.Empty);

            loaded = true;
        }

        void g_OnDataArrive(XboxOneGamepad gamepad, XboxOneButtonData buttons)
        {
            vJoy joystick = joysticks[gamepad.Index - 1];
            if (joystick == null)
            {
                return;
            }

            iReport.bDevice = (byte)gamepad.Index;

            ushort umax = ushort.MaxValue;
            short middle = short.MaxValue / 2;

            iReport.AxisX = buttons.stick_left_x + middle;
            iReport.AxisY = -buttons.stick_left_y + middle;
            iReport.AxisXRot = buttons.stick_right_x + middle;
            iReport.AxisYRot = -buttons.stick_right_y + middle;

            float leftTrigger = buttons.trigger_left / 2048.0f; // max = 1024
            float rightTrigger = buttons.trigger_right / 2048.0f; // max = 1024
            iReport.AxisZ = (int)(leftTrigger * umax);
            iReport.AxisZRot = (int)(rightTrigger * umax);

            int angle = 0;
            if (buttons.dpad_right)
            {
                if (buttons.dpad_up)
                {
                    angle += 45;
                }
                else if (buttons.dpad_down)
                {
                    angle += 135;
                }
                else
                {
                    angle += 90;
                }
            }
            else if (buttons.dpad_down)
            {
                if (buttons.dpad_left)
                {
                    angle += 225;
                }
                else
                {
                    angle += 180;
                }
            }
            else if (buttons.dpad_left)
            {
                if (buttons.dpad_up)
                {
                    angle += 315;
                }
                else
                {
                    angle += 270;
                }
            }

            if (!buttons.dpad_right && !buttons.dpad_up &&
                !buttons.dpad_left && !buttons.dpad_down)
            {
                iReport.bHats = 0xFFFFFFFF;
            }
            else
            {
                // multiply by 100
                iReport.bHats = (uint)(angle * 100);
            }

            // Some bitwise magic:
            BitArray bit = new BitArray(new bool[]
                {
                    buttons.a,
                    buttons.b,
                    buttons.x,
                    buttons.y,
                    buttons.bumper_left,
                    buttons.bumper_right,
                    buttons.back,
                    buttons.start,
                    buttons.stick_left_click,
                    buttons.stick_right_click,
                    buttons.centerButton,
                    triggerButtons ? buttons.trigger_left > 0.1f : false,
                    triggerButtons ? buttons.trigger_right > 0.1f : false
                });
            int[] result = new int[1];
            bit.CopyTo(result, 0);
            iReport.Buttons = (uint)result[0];

            joystick.UpdateVJD((uint)gamepad.Index, ref iReport);
        }

        void g_OnConnectedChanged(XboxOneGamepad data, bool connected)
        {
            vJoy joystick = joysticks[data.Index - 1];
            if (!connected && joystick != null)
            {
                DisableVJoy(joystick, data);
            }

            if (connected)
            {
                gamepadStatus[data.Index - 1].Image = Resources.xbox_one_logo;
            }
            else
            {
                gamepadStatus[data.Index - 1].Image = Resources.xbox_one_logo_dark;
            }
        }

        private static void DisableVJoy(vJoy joystick, XboxOneGamepad gamepad)
        {
            // stop
            if (joystick != null)
            {
                joystick.RelinquishVJD((uint)gamepad.Index);
                joystick = null;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (!loaded)
            {
                return;
            }

            for (int i = 0; i < joysticks.Length; i++)
            {
                XboxOneGamepad gamepad = XboxOneGamepad.GetGamepad(i);
                if (gamepad.Connected)
                {
                    vJoy joystick = joysticks[i];
                    if (joystick != null)
                    {
                        joystick.SetBtn(gamepad.Data.a, (uint)gamepad.Index, 1);
                    }
                }
                else
                {
                    gamepadStatus[i].Image = Resources.xbox_one_logo_dark;
                }
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            triggerButtons = checkBox1.Checked;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < joysticks.Length; i++)
            {
                XboxOneGamepad gamepad = XboxOneGamepad.GetGamepad(i);
                if (gamepad.Connected)
                {
                    vJoy joystick = joysticks[i];
                    if (joystick == null)
                    {
                        joystick = new vJoy();
                        if (!joystick.vJoyEnabled())
                        {
                            MessageBox.Show("VJoy not enabled!");
                            this.Close();
                        }

                        uint id = (uint)(i + 1);
                        VjdStat status = joystick.GetVJDStatus(id);

                        // Acquire the target
                        if ((status == VjdStat.VJD_STAT_OWN) || ((status == VjdStat.VJD_STAT_FREE) && (!joystick.AcquireVJD(id))))
                        {
                            gamepadStatus[i].Image = Resources.xbox_one_logo_darker;
                            continue;
                        }
                        joystick.ResetVJD(id);
                        joysticks[i] = joystick;

                        gamepadStatus[i].Image = Resources.xbox_one_logo;
                    }
                }
                else
                {
                    gamepad.Connect();
                    if (gamepad.Connected)
                    {
                        gamepadStatus[i].Image = Resources.xbox_one_logo;
                    }
                    else
                    {
                        gamepadStatus[i].Image = Resources.xbox_one_logo_dark;
                    }
                }
            }
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            XboxOneGamepad.DestroyAll();
            for (int i = 0; i < 16; i++)
            {
                XboxOneGamepad gamepad = XboxOneGamepad.GetGamepad(i);
                DisableVJoy(joysticks[i], gamepad);
            }
        }

        private void showConnectedGamepadsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowConnectedGamepads pads = new ShowConnectedGamepads();
            pads.Show();
        }

        private void btn_steam_Click(object sender, EventArgs e)
        {
            SteamFolders folders = new SteamFolders();
            folders.ShowDialog();
            ReloadList();
        }
    }
}
